#ifndef _PYOPENSG_WRAPPERS_H_
#define _PYOPENSG_WRAPPERS_H_

#include <boost/python.hpp>
#include <OpenSG/OSGMatrix.h>
#include <OpenSG/OSGVector.h>
#include <OpenSG/OSGQuaternion.h>
#include <OpenSG/OSGFieldContainerPtr.h>
#include <OpenSG/OSGCamera.h>
#include <OpenSG/OSGAction.h>

namespace bp = boost::python;

/** Wrapper methods for getting values from some types. */
namespace osgwrap
{

bool osgInitWrapper(bp::list pythonArgv);

OSG::ActionBase::ResultE traverseWrapper1(OSG::NodePtrConstArg root,
                                          bp::object func);

OSG::ActionBase::ResultE traverseWrapper2(bp::list nodeList, bp::object func);

OSG::ActionBase::ResultE traverseWrapper3(OSG::NodePtrConstArg root,
                                          bp::object enterFunc,
                                          bp::object exitFunc);

OSG::ActionBase::ResultE traverseWrapper4(bp::list nodeList,
                                          bp::object enterFunc,
                                          bp::object exitFunc);

OSG::Int32 ptrGetRefCount(OSG::FieldContainerPtr ptr);

OSG::UInt32 ptrGetContainerId(OSG::FieldContainerPtr ptr);

bool isNullFC(OSG::FieldContainerPtr ptr);


template<typename T>
boost::python::tuple getValues2(T* obj)
{
   return boost::python::make_tuple((*obj)[0], (*obj)[1]);
}

template<typename T>
boost::python::tuple getValues3(T* obj)
{
   return boost::python::make_tuple((*obj)[0], (*obj)[1], (*obj)[2]);
}

template<typename T>
boost::python::tuple getValues4(T* obj)
{
   return boost::python::make_tuple((*obj)[0], (*obj)[1], (*obj)[2], (*obj)[3]);
}

/*
template<unsigned int SIZE, typename T>
boost::python::tuple getValues(T* obj)
{
   boost::python::tuple result(SIZE);

   for ( unsigned int i; i < SIZE; ++i )
   {
      tuple[i] = (*obj)[i];
   }

   return result;
}
*/

#if defined(__GCCXML__)
template boost::python::tuple getValues3(OSG::Pnt3f*);
template boost::python::tuple getValues4(OSG::Pnt4f*);

template boost::python::tuple getValues4(OSG::Quaternion*);
template boost::python::tuple getValues4(OSG::Quaterniond*);

template boost::python::tuple getValues3(OSG::Vec3f*);
template boost::python::tuple getValues4(OSG::Vec4f*);
#endif

template<typename T, unsigned SIZE>
static boost::python::list makeDataList(T* data)
{
   boost::python::list l;

   for ( unsigned int i = 0; i < SIZE; ++i )
   {
      l.append(data[i]);
   }

   return l;
}

template<typename T>
boost::python::list getValuesMatrix(OSG::TransformationMatrix<T>* matrix)
{
   return makeDataList<T, 16>(matrix->getValues());
}

template<typename T>
boost::python::tuple getTransform_Matrix(OSG::TransformationMatrix<T>* matrix)
{
   typename OSG::TransformationMatrix<T>::VectorType3f trans, scale;
   typename OSG::TransformationMatrix<T>::QuaternionType rotation, scaleOrientation;
   matrix->getTransform(trans, rotation, scale, scaleOrientation);

   return boost::python::make_tuple(trans,rotation,scale,scaleOrientation);
}


#if defined(__GCCXML__)
template boost::python::list getValuesMatrix(OSG::Matrix*);
template boost::python::list getValuesMatrix(OSG::Matrix4d*);
#endif

template<typename T, typename S>
void setArrayElement(T* obj, const unsigned int i, S value)
{
   (*obj)[i] = value;
}


template<typename T>
void setValueMatrix(OSG::TransformationMatrix<T>* matrix,
                    boost::python::list pyData, bool transposed = true)
{
   T mat_data[16];

   for ( unsigned int i = 0; i < 16; ++i )
   {
      mat_data[i] = boost::python::extract<T>(pyData[i]);
   }

   matrix->setValue(mat_data, transposed);
}

//XXX: Currently there is a bug (#5439) in gccxml that does all explicit member template
//     instantiation of templated classes. For now we will have to manually wrap these methods.
template<typename L, typename R>
void wrapMatrixMult(OSG::TransformationMatrix<L>* left, const OSG::TransformationMatrix<R>& right)
{
   left->mult(right);
}

template<typename L, typename R>
void wrapMatrixMultLeft(OSG::TransformationMatrix<L>* left, const OSG::TransformationMatrix<R>& right)
{
   left->multLeft(right);
}

#if defined(__GCCXML__)
template void wrapMatrixMult(OSG::TransformationMatrix<OSG::Real32>*, const OSG::TransformationMatrix<OSG::Real32>&);
template void wrapMatrixMult(OSG::TransformationMatrix<OSG::Real32>*, const OSG::TransformationMatrix<OSG::Real64>&);
template void wrapMatrixMult(OSG::TransformationMatrix<OSG::Real64>*, const OSG::TransformationMatrix<OSG::Real32>&);
template void wrapMatrixMult(OSG::TransformationMatrix<OSG::Real64>*, const OSG::TransformationMatrix<OSG::Real64>&);
template void wrapMatrixMultLeft(OSG::TransformationMatrix<OSG::Real32>*, const OSG::TransformationMatrix<OSG::Real32>&);
template void wrapMatrixMultLeft(OSG::TransformationMatrix<OSG::Real32>*, const OSG::TransformationMatrix<OSG::Real64>&);
template void wrapMatrixMultLeft(OSG::TransformationMatrix<OSG::Real64>*, const OSG::TransformationMatrix<OSG::Real32>&);
template void wrapMatrixMultLeft(OSG::TransformationMatrix<OSG::Real64>*, const OSG::TransformationMatrix<OSG::Real64>&);
#endif

/*
OSG::Matrixr Camera_getProjection(   OSG::UInt32         width, OSG::UInt32         height)
{
   Matrixr temp_mat;
   this->getProjection(temp_mat, width,height);
   return temp_mat;
}

Matrixr Camera::getProjectionTranslation(   OSG::UInt32         width,
                                          OSG::UInt32         height)
{
   Matrixr temp_mat;
   this->getProjectionTranslation(temp_mat, width, height);
   return temp_mat;
}

Matrixr Camera::getViewing              ( OSG::UInt32         width,
                                          OSG::UInt32         height)
{
   Matrixr temp_mat;
   this->getViewing(temp_mat, width, height);
   return temp_mat
}

FrustumVolume Camera::getFrustum        (  const OSG::Viewport      &port  )
{
   FrustumVolume vol;
   this->getFrustum(vol, port);
   return vol
}

Matrixr Camera::getWorldToScreen        ( const OSG::Viewport      &port  )
{
   Matrixr temp_mat;
   this->getWorldToScreen(temp_mat, port);
   return temp_mat;
}
*/


/*
template void setArrayElement(OSG::Pnt3f*, const unsigned int, OSG::Real32);
template void setArrayElement(OSG::Pnt4f*, const unsigned int, OSG::Real32);

template void setArrayElement(OSG::Quaternion*, const unsigned int, OSG::Real32);

template void setArrayElement(OSG::Vec3f*, const unsigned int, OSG::Real32);
template void setArrayElement(OSG::Vec4f*, const unsigned int, OSG::Real32);
*/


// Quaternion wrapper methods
template<typename ValueType>
boost::python::tuple getValueAsAxisDeg(OSG::QuaternionBase<ValueType>* quat)
{
   typedef OSG::VectorInterface < ValueType, OSG::VecStorage3<ValueType>  > vec_t;

   vec_t axis;
   ValueType degs;
   quat->getValueAsAxisDeg(axis,degs);
   return boost::python::make_tuple(axis,degs);
   /*
   OSG::Real32 x, y, z, w;
   quat->getValueAsAxisDeg(x, y, z, w);
   return boost::python::make_tuple(x,y,z,w);
   */
}


template<typename ValueType>
boost::python::tuple getValueAsAxisRad(OSG::QuaternionBase<ValueType>* quat)
{
   typedef OSG::VectorInterface < ValueType, OSG::VecStorage3<ValueType>  > vec_t;

   vec_t axis;
   ValueType rads;
   quat->getValueAsAxisDeg(axis,rads);
   return boost::python::make_tuple(axis,rads);
   
   /*
   OSG::Real32 x, y, z, w;
   quat->getValueAsAxisRad(x, y, z, w);
   return boost::python::make_tuple(x,y,z,w);
   */
}

#if defined(__GCCXML__)
template boost::python::tuple getValueAsAxisDeg(OSG::Quaternion*);
template boost::python::tuple getValueAsAxisDeg(OSG::Quaterniond*);

template boost::python::tuple getValueAsAxisRad(OSG::Quaternion*);
template boost::python::tuple getValueAsAxisRad(OSG::Quaterniond*);
#endif


// NOTE: This does not take a const reference to an OSG::Viewport object to
// avoid complications resulting from OSG::Viewport having a protected
// destructor. (We do not want Boost.Pythoon trying to make a copy of an
// OSG::Viewport behind the scenes.)
boost::python::tuple calcViewRay(OSG::Camera* self, OSG::Int32 x,
                                 OSG::Int32 y, OSG::Viewport& port);

boost::python::tuple getNormalizedCoordinates(OSG::Viewport* self,
                                              const OSG::Int32 vpX,
                                              const OSG::Int32 vpY);

/**
 * Guard to handle releasing the GIL when calling into long running C++ code.
 */
class ReleaseGILGuard
{
public:
   ReleaseGILGuard()
      : mLocked(false)
      , mSave(NULL)
   {
      mSave = PyEval_SaveThread();
      mLocked = true;
   }

   ~ReleaseGILGuard()
   {
      PyEval_RestoreThread(mSave);
      mLocked = false;
   }

private:
   bool mLocked;
   PyThreadState* mSave;
};

}

#endif /* _PYOPENSG_WRAPPERS_H_ */
